using System;
using System.Collections;
using System.Threading;
using System.Windows.Forms;
using Agent;

public class modMain
{
	#region " Constants "

		// random port number
	public static readonly int PortNum = 10000;
		// bytes
	public static readonly int BufferSize = 4096;

	#region " Our simple text message protocol "

	// Protocol: 
	// these constants describe how our plain text messages are being transferred over TCP
	// in our application.
	// E.g. 
	// "CONNECT|James|JIMPC", 
	// "STATUS|ONLINE", 
	// "CHAT|Hi, there!"
		// when initiating connection
	public static readonly string msgConnect = "CONNECT";
		// text message
	public const string msgChat = "CHAT";

		// change status notification (Online / Do Not Disturb)
	public const string msgStatus = "STATUS";
	public const string msgStatusOnline = "ONLINE";
	public const string msgStatusDoNotDisturb = "DONOTDISTURB";

	#endregion

	// Color settings: too lazy to make these runtime-editable :-)
		// user's message text
	public static readonly System.Drawing.Color colUser = System.Drawing.Color.Red;
		// my message text
	public static readonly System.Drawing.Color colMe = System.Drawing.Color.Blue;

	#endregion

	private static Chat mChatServer;
	public static Chat ChatServer
	{
		get
		{
			return mChatServer;
		}
		set
		{
			if (mChatServer != null)
			{
				mChatServer.UserConnected -= ChatServer_UserConnected;
				mChatServer.UserDisconnected -= ChatServer_UserDisconnected;
				mChatServer.UserReceivedText -= ChatServer_UserReceivedText;
				mChatServer.UserStatusChanged -= ChatServer_UserStatusChanged;
			}
			mChatServer = value;
			if (mChatServer != null)
			{
				mChatServer.UserConnected += ChatServer_UserConnected;
				mChatServer.UserDisconnected += ChatServer_UserDisconnected;
				mChatServer.UserReceivedText += ChatServer_UserReceivedText;
				mChatServer.UserStatusChanged += ChatServer_UserStatusChanged;
			}
		}
	}

	private static MessageWindow ChatWindow;

	#region " Main(), initialization & termination "

		// globally visible object
	public static Mutex InstanceMutex;

	public static void Main()
	{
		if (IsAlreadyRunning()) return; 
		// check if another instance already running

		Initialize();

		ChatWindow = new MessageWindow();
		ChatWindow.InitializeWindow();
		ChatWindow.ShouldBroadcastText += new MessageWindow.ShouldBroadcastTextEventHandler(ChatWindow_ShouldBroadcastText);
		ChatServer = Chat.Instance;
		// Save a reference to the Chat Singleton for future use.
		ChatServer.Start();

		Application.Run();

		Quit();
		InstanceMutex.Close();
	}

	#region " IsAlreadyRunning "

	//====================================================================
	// This code section determines, if another instance of this process
	// already runs on this machine.
	//====================================================================

	public static bool IsAlreadyRunning()
	{
		bool functionReturnValue = false;
		// The name of the global mutex should be unique not to mix with other applications.
		const string UniqueString = "LANChatAgentByKirillOsenkov";

		// This is the flag to be filled with a boolean result: 
		// True when no instance existed and a new one was just created
		// False when an instance already existed and no action was taken.
		bool createdNew = false;

		// try to create a mutex and see, if it already exists
		InstanceMutex = new Mutex(false, UniqueString, out createdNew);
		return !createdNew;
		return functionReturnValue;
	}

	//====================================================================

	#endregion

	public static void Quit()
	{
		Tray.Visible = false;
		if ((ChatServer != null))
		{
			ChatServer.Dispose();
			ChatServer = null;
		}
		if ((ChatWindow != null))
		{
			ChatWindow.Dispose();
			ChatWindow = null;
		}
	}

	#endregion

	#region " Menu & Tray "

	#region " Variable declarations "

	//====================================================================
	internal static System.Windows.Forms.ContextMenu mnuPopup;

	internal static System.Windows.Forms.MenuItem mnuAddUser;
	internal static System.Windows.Forms.MenuItem mnuContactList;
	internal static System.Windows.Forms.MenuItem mnuShowChat;
	internal static System.Windows.Forms.MenuItem mnuDoNotDisturb;
	internal static System.Windows.Forms.MenuItem mnuExit;

	internal static System.Windows.Forms.MenuItem mnuSep1;
	internal static System.Windows.Forms.MenuItem mnuSep2;
	internal static System.Windows.Forms.MenuItem mnuSep3;

	internal static System.Windows.Forms.NotifyIcon Tray;
	//====================================================================
	#endregion

	private static void Initialize()
	{
		PreloadIcons();

		mnuPopup = new System.Windows.Forms.ContextMenu();
		mnuShowChat = new System.Windows.Forms.MenuItem();
		mnuSep1 = new System.Windows.Forms.MenuItem();
		mnuAddUser = new System.Windows.Forms.MenuItem();
		mnuContactList = new System.Windows.Forms.MenuItem();
		mnuSep2 = new System.Windows.Forms.MenuItem();
		mnuDoNotDisturb = new System.Windows.Forms.MenuItem();
		mnuSep3 = new System.Windows.Forms.MenuItem();
		mnuExit = new System.Windows.Forms.MenuItem();
		Tray = new System.Windows.Forms.NotifyIcon();

		mnuPopup.MenuItems.AddRange(new System.Windows.Forms.MenuItem[] {mnuShowChat, mnuSep1, mnuAddUser, mnuContactList, mnuSep2, mnuDoNotDisturb, mnuSep3, mnuExit});

		mnuShowChat.DefaultItem = true;
		mnuShowChat.Text = "Show chat";
		mnuSep1.Text = "-";
		mnuAddUser.Text = "Add user";
		mnuContactList.Text = "Contact list";
		mnuSep2.Text = "-";
		mnuDoNotDisturb.Text = "Do not disturb";
		mnuSep3.Text = "-";
		mnuExit.Text = "Exit";

		mnuShowChat.Click += new EventHandler(mnuShowChat_Click);
		mnuAddUser.Click += new EventHandler(mnuAddUser_Click);
		mnuContactList.Click += new EventHandler(mnuContactList_Click);
		mnuDoNotDisturb.Click += new EventHandler(mnuDoNotDisturb_Click);
		mnuExit.Click += new EventHandler(mnuExit_Click);

		Tray.MouseDown += new MouseEventHandler(Tray_MouseDown);

		Tray.ContextMenu = mnuPopup;
		SetTrayIcon(TrayIcon.LocalOnUsersOff);
		Tray.Text = "User Offline";
		Tray.Visible = true;
	}

	// This happens when left-clicking the icon in system tray.
	private static void Tray_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
	{
		if (e.Button == MouseButtons.Left)
		{
			ToggleChatWindow();
		}
	}

	// Auxiliary sub to show or hide the message window. 
	// Thread-safe because calls our protected MessageMinimized property.
	private static void ToggleChatWindow()
	{
		MessageMinimized = !MessageMinimized;
	}

	//====================================================================

	#region " Menu events "

	//====================================================================
	// Context menu events
	//====================================================================

	private static void mnuContactList_Click(object sender, System.EventArgs e)
	{
		ContactList ContactListDialog = new ContactList();
		foreach (RemoteUser user in ChatServer.Users) {
			ContactListDialog.lstUsers.Items.Add(user.Name + ": " + user.Status.ToString());
		}
		ContactListDialog.ShowDialog();
	}

	//====================================================================

	private static void mnuAddUser_Click(object sender, System.EventArgs e)
	{
		AddUser addUserDialog = new AddUser();
		if (addUserDialog.ShowDialog() != DialogResult.OK) return;

		string UserName = addUserDialog.txtName.Text;
		string UserAddress = addUserDialog.txtAddress.Text;

		if ((ChatServer.Users[UserName] != null))
		{
			MessageBox.Show("User " + UserName + " already exists.");
		}
		else
		{
			ChatServer.Users.AddUserOutgoingRequest(UserName, UserAddress);
		}
	}

	//====================================================================

	private static void mnuShowChat_Click(object sender, System.EventArgs e)
	{
		ToggleChatWindow();
	}

	//====================================================================

	private static void mnuExit_Click(object sender, System.EventArgs e)
	{
		Application.Exit();
	}

	//====================================================================

	private static void mnuDoNotDisturb_Click(object sender, System.EventArgs e)
	{
		bool NA = !mnuDoNotDisturb.Checked;

		mnuDoNotDisturb.Checked = NA;
		if (NA != DoNotDisturb)
		{
			DoNotDisturb = NA;
		}
	}

	//====================================================================

	#endregion

	//====================================================================

	#region " Tray Icons & DND Mode "

	//====================================================================
	// Tray icons
	//====================================================================

	private enum TrayIcon
	{
		LocalOnUsersOff,
		LocalOnUsersNA,
		LocalOnUsersOn,
		LocalNAUsersOff,
		LocalNAUsersNA,
		LocalNAUsersOn
	}

	private static Hashtable IconTable = new Hashtable();

	private static void PreloadIcons()
	{
		Type TypeInThisAssembly = typeof(Chat);
		// any type in the assembly with the icons
		IconTable.Add(TrayIcon.LocalOnUsersOn, Resources.OnOn);
		IconTable.Add(TrayIcon.LocalOnUsersNA, Resources.OnNa);
		IconTable.Add(TrayIcon.LocalOnUsersOff, Resources.OnOff);
		IconTable.Add(TrayIcon.LocalNAUsersOn, Resources.NaOn);
		IconTable.Add(TrayIcon.LocalNAUsersNA, Resources.NaNa);
		IconTable.Add(TrayIcon.LocalNAUsersOff, Resources.NaOff);
	}

	private static void UpdateTrayIcon()
	{
		if (ChatServer.Users.AnyUsersOnline())
		{
			if (ChatServer.Users.AllUsersNotAvailable())
			{
				if (DoNotDisturb)
				{
					SetTrayIcon(TrayIcon.LocalNAUsersNA);
				}
				else
				{
					SetTrayIcon(TrayIcon.LocalOnUsersNA);
				}
			}
			else
			{
				if (DoNotDisturb)
				{
					SetTrayIcon(TrayIcon.LocalNAUsersOn);
				}
				else
				{
					SetTrayIcon(TrayIcon.LocalOnUsersOn);
				}
			}
		}
		else
		{
			if (DoNotDisturb)
			{
				SetTrayIcon(TrayIcon.LocalNAUsersOff);
			}
			else
			{
				SetTrayIcon(TrayIcon.LocalOnUsersOff);
			}
		}
	}

	private static void SetTrayIcon(TrayIcon WhichIcon)
	{
		Tray.Icon = (System.Drawing.Icon)IconTable[WhichIcon];
	}

	#region " DoNotDisturb "

	private static bool mDoNotDisturb = false;
	public static bool DoNotDisturb {
		get { return mDoNotDisturb; }
		set {
			mDoNotDisturb = value;
			if (value)
			{
				ChatServer.Users.SendStatus(UserStatus.DoNotDisturb);
			}
			else
			{
				ChatServer.Users.SendStatus(UserStatus.Online);
			}
			UpdateTrayIcon();
		}
	}

	#endregion

	#endregion

	#endregion

	#region " UI interaction "

	//====================================================================

	#region " ChatServer Events "

	//====================================================================
	// Chat events.
	// Use only thread-safe UI interaction.
	//====================================================================

	private static void ChatServer_UserConnected(RemoteUser Sender)
	{
		Log("Connected to user: " + Sender.Name, System.Drawing.Color.Black);
		UpdateStatusText(Sender.Name + ": Online");

		if (DoNotDisturb) Sender.SendStatus(UserStatus.DoNotDisturb); 
	}

	//====================================================================

	private static void ChatServer_UserDisconnected(RemoteUser Sender)
	{
		modMain.Log("Disconnected user: " + Sender.Name, System.Drawing.Color.Black);
		UpdateStatusText(Sender.Name + ": Offline");
	}

	//====================================================================

	private static void ChatServer_UserReceivedText(RemoteUser Sender, string Text)
	{
		Log(Sender.Name + ": " + Text, colUser);
		ShowChatWindow();
	}

	//====================================================================

	private static void ChatServer_UserStatusChanged(RemoteUser Sender)
	{
		UpdateStatusText(Sender.Name + " status: " + Sender.Status.ToString());
	}

	//====================================================================

	#endregion

	// This event is raised when the user clicks Send button of the MessageWindow form.
	private static void ChatWindow_ShouldBroadcastText(string Text)
	{
		ChatServer.Users.SendText(Text);
	}

	#region " Thread-safe UI interaction "

	//====================================================================
	// Thread safe UI-Interaction

	// You cannot call UI Methods, such as Log, from another thread,
	// you need to use the Invoke mechanismus for this purpose.
	//====================================================================

	#region " Log "

	//====================================================================
	// Log can be called from another thread, e.g. Listener thread (Class LocalPort)
	// or any thread from the thread pool.
	//====================================================================

	private delegate void LogDelegate(string Text, System.Drawing.Color ForeColor);

	private static void Log(string Text, System.Drawing.Color ForeColor)
	{
		if (ChatWindow == null) return; // TODO: might not be correct. Was : Exit Sub
 

		// check if the caller's thread isn't the main UI thread of the application.
		if (!ChatWindow.InvokeRequired)
		{
			ChatWindow.Log(Text, ForeColor);
		}
		else
		{
			// indirect intra-thread method invocation
			System.Delegate d = new LogDelegate(Log);
			ChatWindow.BeginInvoke(d, new object[] {Text, ForeColor});
		}
	}

	#endregion

	#region " UpdateStatusText "

	private delegate void UpdateStatusTextDelegate(string Text);

	private static void UpdateStatusText(string Text)
	{
		if (ChatWindow == null) return; // TODO: might not be correct. Was : Exit Sub
 

		if (!ChatWindow.InvokeRequired)
		{
			ChatWindow.Text = Text;
			Tray.Text = Text;
			UpdateTrayIcon();
		}
		else
		{
			System.Delegate d = new UpdateStatusTextDelegate(UpdateStatusText);
			ChatWindow.BeginInvoke(d, new object[] {Text});
		}
	}

	#endregion

	#region " Minimize "

	private static void ShowChatWindow()
	{
		if (!DoNotDisturb) MessageMinimized = false; 
	}

	// This property is thread safe and can be called from another threads.
	private static bool mMessageMinimized = true;
	public static bool MessageMinimized {
		get { return ChatWindow.MinimizedToTray; }
		set {
			mMessageMinimized = value;
			SetMinimizedState(value);
		}
	}

	private delegate void SetMinimizedStateDelegate(bool Minimized);
	private static void SetMinimizedState(bool Minimized)
	{
		if (!ChatWindow.InvokeRequired)
		{
			ChatWindow.MinimizedToTray = Minimized;
		}
		else
		{
			System.Delegate ChangeWindowStateDelegate = new SetMinimizedStateDelegate(SetMinimizedState);
			// Asynchronous call, this thread doesn't need to wait for the UI thread to finish.
			ChatWindow.BeginInvoke(ChangeWindowStateDelegate, new object[] {Minimized});
		}
	}

	#endregion

	//====================================================================

	#endregion

	//====================================================================

	#endregion

}
